package nacos

import (
	"errors"
	"strconv"
	"strings"
	"sync"
	"time"

	"github.com/nacos-group/nacos-sdk-go/util"
	"github.com/sirupsen/logrus"

	"github.com/nacos-group/nacos-sdk-go/clients"
	"github.com/nacos-group/nacos-sdk-go/clients/naming_client"
	"github.com/nacos-group/nacos-sdk-go/common/constant"
	"github.com/nacos-group/nacos-sdk-go/vo"
)

type NacosConfig struct {
	ServiceName     []string
	RefreshInterval int
	ServerConfs     []ServerConfig
	ClientConf      ClientConfig
}

type ServerConfig struct {
	IpAddr      string
	ContextPath string
	Port        uint64
}

type ClientConfig struct {
	NamespaceId         string `env:"PROFILE"`
	NamespaceIdToUpper  bool
	TimeoutMs           uint64
	NotLoadCacheAtStart bool
	LogDir              string
	CacheDir            string
	RotateTime          string
	MaxAge              int64
	LogLevel            string
}

type RegisterList struct {
	serverList map[string]chan string
	lock       sync.RWMutex
}

type NacosClient struct {
	NamingClient naming_client.INamingClient
	RegisterList *RegisterList
}

/**
 * @Description  创建nacos客户端
 */
func NewNacosClient(c *NacosConfig) (*NacosClient, error) {
	if c.ClientConf.NamespaceIdToUpper {
		c.ClientConf.NamespaceId = strings.ToUpper(c.ClientConf.NamespaceId)
	}
	var scs []constant.ServerConfig
	for _, v := range c.ServerConfs {
		sc := constant.ServerConfig{
			IpAddr:      v.IpAddr,
			ContextPath: v.ContextPath,
			Port:        v.Port,
		}
		scs = append(scs, sc)
	}
	cc := constant.ClientConfig{
		NamespaceId:         c.ClientConf.NamespaceId, //namespace id
		TimeoutMs:           c.ClientConf.TimeoutMs,
		NotLoadCacheAtStart: c.ClientConf.NotLoadCacheAtStart,
		LogDir:              c.ClientConf.LogDir,
		CacheDir:            c.ClientConf.CacheDir,
		LogLevel:            c.ClientConf.LogLevel,
	}
	namingClient, err := clients.CreateNamingClient(map[string]interface{}{
		"serverConfigs": scs,
		"clientConfig":  cc,
	})
	if err != nil {
		return nil, err
	}

	return &NacosClient{
		namingClient,
		newRegisterServList(c, namingClient),
	}, nil
}

/**
 * @Description  注册实例
 */
func (cli *NacosClient) RegisterInstance(serviceName string, port uint64) error {
	success, err := cli.NamingClient.RegisterInstance(vo.RegisterInstanceParam{
		Ip:          util.LocalIP(),
		Port:        port,
		ServiceName: serviceName,
		Weight:      10,
		Enable:      true,
		Healthy:     true,
		Ephemeral:   true,
		Metadata:    map[string]string{"modules": serviceName},
	})
	if err != nil {
		logrus.Info(err)
		return err
	}
	if success {
		logrus.Infof("register instance[%s] successful", serviceName)
	}
	return nil
}

func (cli *NacosClient) DeRegisterInstance(serviceName, ip string, port uint64) error {
	success, err := cli.NamingClient.DeregisterInstance(vo.DeregisterInstanceParam{
		Ip:          ip,
		Port:        port,
		ServiceName: serviceName,
		Ephemeral:   true,
	})
	if err != nil {
		logrus.Info(err)
		return err
	}
	if success {
		logrus.Infof("deregister instance[%s] successful", serviceName)
	}
	return nil
}

func newRegisterServList(conf *NacosConfig, namingClient naming_client.INamingClient) *RegisterList {
	registerList := &RegisterList{
		lock: sync.RWMutex{},
	}
	registerList.refresh(conf, namingClient)
	go func() {
		for true {
			time.Sleep(time.Duration(conf.RefreshInterval) * time.Millisecond)
			registerList.refresh(conf, namingClient)
		}
	}()

	return registerList
}

func (registerList *RegisterList) refresh(conf *NacosConfig, namingClient naming_client.INamingClient) {
	for _, serviceName := range conf.ServiceName {
		instances, err := namingClient.SelectInstances(vo.SelectInstancesParam{
			ServiceName: serviceName, //required
			HealthyOnly: true,
		})
		if err != nil {
			logrus.Errorf("serviceName: [%s], err: [%v]", serviceName, err)
		}
		if len(instances) > 0 {
			newChan := make(chan string, len(instances))
			for i := 0; i < len(instances); i++ {
				addr := instances[i].Ip + ":" + strconv.FormatUint(instances[i].Port, 10)
				newChan <- addr
			}

			registerList.lock.Lock()
			if registerList.serverList == nil {
				registerList.serverList = map[string]chan string{}
			}
			registerList.serverList[serviceName] = newChan
			registerList.lock.Unlock()
			//logrus.Infof("loaded healthy instances from nacos, serviceName: %s", serviceName)
		}
	}
}

/**
 * @Description  根据服务注册名返回 "ip:port"
 */
func (registerList *RegisterList) GetInstance(serviceName string) (instance string, err error) {
	registerList.lock.RLock()
	defer registerList.lock.RUnlock()
	if serverListChan, ok := registerList.serverList[serviceName]; ok && serverListChan != nil {
		select {
		case instance = <-serverListChan:
			serverListChan <- instance
		default:
			err = errors.New("nacos get instance failed: chan non instances; serviceName: " + serviceName)
		}
	} else {
		err = errors.New("nacos get instance failed: serverList non chan; serviceName: " + serviceName)
	}
	if err != nil {
		logrus.Info(err)
	}
	return
}
